﻿//===================================================================================
// Copyright (c) Ryan Garrett.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
using System;
using System.Collections.Generic;

namespace Caffeine.Core
{
    /// <summary>
    /// Provides extension methods for the Caffeine.Core.ModelProperty type
    /// </summary>
    public static class ModelPropertyExtensions
    {
        /// <summary>
        /// Sync this property to another property
        /// </summary>
        /// <typeparam name="T">Any primitive type. Ex) String, Int32, Decimal, Char, etc.</typeparam>
        /// <param name="propertyToSyncFrom">Current property</param>
        /// <param name="propertyToSyncTo">Sync changes to this property</param>
        public static void SyncTo<T>(this ModelProperty<T> propertyToSyncFrom, ModelProperty<T> propertyToSyncTo)
        {
            propertyToSyncFrom.SyncTo(propertyToSyncTo, false);
        }

        /// <summary>
        /// Sync this property to another property
        /// </summary>
        /// <typeparam name="T">Any primitive type. Ex) String, Int32, Decimal, Char, etc.</typeparam>
        /// <param name="propertyToSyncFrom">Current property</param>
        /// <param name="propertyToSyncTo">Sync changes to this property</param>
        /// <param name="updateOnCommit">
        /// True will cause syncing to take place only when the source property is committed. 
        /// False will cause the property to sync whenever the source property's value has changed.
        /// </param>
        public static void SyncTo<T>(this ModelProperty<T> propertyToSyncFrom, ModelProperty<T> propertyToSyncTo, Boolean updateOnCommit)
        {
            if (propertyToSyncTo == null)
            {
                throw new ArgumentNullException("propertyToSyncTo");
            }
            
            if (propertyToSyncFrom == propertyToSyncTo)
            {
                throw new ArgumentException("A property cannot be synced to itself");
            }

            if
            (
                propertyToSyncTo.PropertySync != null &&
                (
                    propertyToSyncTo.PropertySync.SyncedToProperty == propertyToSyncFrom ||
                    propertyToSyncTo.PropertySync.SyncedToPropertyReference.Target == propertyToSyncFrom
                )
            )
            {
                throw new ArgumentException("Cyclical property dependency detected while syncing properties");
            }

            propertyToSyncFrom.PropertySync = new PropertySync<T>(propertyToSyncFrom, propertyToSyncTo, updateOnCommit);
            propertyToSyncFrom.ForceSync(updateOnCommit);
        }

        /// <summary>
        /// Chain an additional validation method to be called after the validation method already assigned is called. If no value is
        /// already assigned to the ValidationMethod property, the <paramref name="validationMethod"/> parameter value will be assigned 
        /// to it.
        /// </summary>
        /// <typeparam name="T">Type of value contained by the property</typeparam>
        /// <param name="property">Property to validate</param>
        /// <param name="validationMethod">Method used to validate property</param>
        public static void ChainValidationMethod<T>(this ModelProperty<T> property, PropertyValidationDel<T> validationMethod)
        {
            if (validationMethod != null)
            {
                if (property.ValidationMethod == null)
                {
                    property.ValidationMethod = validationMethod;
                }
                else
                {
                    property.ValidationMethod = (new PropertyValidationMethodWrapper<T>(property.ValidationMethod, validationMethod)).ValidationMethod;
                }
            }
        }
    }
}
